package com.richinfo.nio.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * Nio服务器端
 * 
 * Created by WangXJ
 * 2019-07-10 14:57
 */
public class NioServer {

	/**
	 * 启动
	 */
	public void start() throws IOException {
		// 1.创建Selector
		Selector selector = Selector.open();
		// 2.通过ServerSocketChannel创建channel通道
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		// 3.为channel通道绑定监听端口
		serverSocketChannel.bind(new InetSocketAddress(8000));
		// 4.设置channel为非阻塞模式
		serverSocketChannel.configureBlocking(false);
		// 5.将channel注册到selector上，监听 连接事件
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("======== 服务器-启动成功 ========");
		// 6.循环等待新接入的连接
		while (true) {
			// 获取可用的channel数量
			int readyChannels = selector.select();
			// 防止空轮询
			if (readyChannels == 0) continue;
			// 获取可用的channel集合
			Set<SelectionKey> selectionKeys = selector.selectedKeys();
			Iterator<SelectionKey> iterator = selectionKeys.iterator();
			while (iterator.hasNext()) {
				// 获取当前selectionKey实例
				SelectionKey selectionKey = iterator.next();
				// 把当前selectionKey从set集中移除
				iterator.remove();
				
				// 7.根据就绪状态，调用对应方法处理业务逻辑
				// 如果是  接入事件
				if (selectionKey.isAcceptable()) {
					acceptHandler(serverSocketChannel, selector);
				}
				// 如果是  可读事件
				if (selectionKey.isReadable()) {
					readHandler(selectionKey, selector);
				}
			}
		}
	}
	
	/**
	 * 接入事件处理器
	 * @throws IOException 
	 */
	private void acceptHandler(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
		// 如果是接入事件，创建socketChannel(客户端用)
		SocketChannel socketChannel = serverSocketChannel.accept();
		// 将socketChannel设置为非阻塞模式
		socketChannel.configureBlocking(false);
		// TODO 将channel注册到selector上，监听  可读事件
		socketChannel.register(selector, SelectionKey.OP_READ);
		// 回复客户端
		socketChannel.write(Charset.forName("UTF-8").encode("你与聊天室其他人都不是朋友关系，请注意隐私安全"));
		
	}
	
	/**
	 * 可读事件处理器
	 */
	private void readHandler(SelectionKey selectionKey, Selector selector) throws IOException {
		// 从selectionKey中获取已经就绪的channel
		SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
		// 创建buffer
		ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
		// 循环读取客户端请求信息
		String request = "";
		while (socketChannel.read(byteBuffer) > 0) {
			// 切换buffer为读模式
			byteBuffer.flip();
			// 读取buffer中的内容
			request += Charset.forName("UTF-8").decode(byteBuffer);
			// TODO 将channel再次注册到selector上，监听 可读事件(好像可以省略，有待验证)
			socketChannel.register(selector, SelectionKey.OP_READ);
			// 将客户端发送的请求信息，广播给其他客户端
			if (request.length() > 0) {
//				System.out.println(":: " + request);
				broadCast(selector, socketChannel, request);
			}
		}
	}
	
	/**
	 * 广播消息
	 */
	private void broadCast(Selector selector, SocketChannel sourceChannel, String request) {
		// 1.获取到所有已接入的客户端channel
		Set<SelectionKey> selectionKeys = selector.keys();
		// 2.循环向所有channel广播信息
		selectionKeys.forEach(selectionKey -> {
			Channel targetChannel = selectionKey.channel();
			// 剔除发消息的客户端
			if (targetChannel instanceof SocketChannel &&
					targetChannel != sourceChannel) {
				// 将信息发送到targetChannel客户端
				try {
					((SocketChannel)targetChannel).write(
							Charset.forName("UTF-8").encode(request));
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
	}
	
	public static void main(String[] args) throws IOException {
		NioServer nioServer = new NioServer();
		nioServer.start();
	}
}
















